﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NMF.Optimizations.Linq
{
    public static class OptimizationExtensions
    {


        /// <summary>
        /// Binds the Monad <see cref="IRelationalSource<>"/> to child items
        /// </summary>
        /// <typeparam name="T1">The inner type of the outer monad instance</typeparam>
        /// <typeparam name="T2">The inner type of the inner monad instance</typeparam>
        /// <typeparam name="T3">The inner type of the return monad</typeparam>
        /// <param name="items">The monad instance which children are to be selected</param>
        /// <param name="func">The operator that should be applied in the bind function</param>
        /// <param name="selector">A method that selects the output</param>
        /// <returns>A monad instance with items selected by the given selector function</returns>
        /// <remarks>Please see the documentation of the SelectMany extension method and its use with the LINQ-syntax for more details</remarks>
        public static IObservableSource<T3> SelectMany<T1, T2, T3>(this IObservableSource<T1> items, Func<T1, IObservableSource<T2>> func, Func<T1, T2, T3> selector)
        {
            return new CompositeSource<T1, T2, T3>(items, func, selector);
        }

        /// <summary>
        /// Binds the Monad <see cref="IRelationalSource<>"/> to child items
        /// </summary>
        /// <typeparam name="T1">The inner type of the outer monad instance</typeparam>
        /// <typeparam name="T2">The inner type of the inner monad instance</typeparam>
        /// <typeparam name="T3">The inner type of the return monad</typeparam>
        /// <param name="items">The monad instance which children are to be selected</param>
        /// <param name="func">The operator that should be applied in the bind function</param>
        /// <param name="selector">A method that selects the output</param>
        /// <returns>A monad instance with items selected by the given selector function</returns>
        /// <remarks>Please see the documentation of the SelectMany extension method and its use with the LINQ-syntax for more details</remarks>
        public static IObservableSource<T3> SelectMany<T1, T2, T3>(this IObservableSource<T1> items, Func<T1, IEnumerable<T2>> func, Func<T1, T2, T3> selector)
        {
            return new SelectManySource<T1, T2, T3>(items, func, selector);
        }

        /// <summary>
        /// Binds the Monad <see cref="IRelationalSource<>"/> to child items
        /// </summary>
        /// <typeparam name="T1">The inner type of the monad instance</typeparam>
        /// <typeparam name="T2">The inner type of the return monad</typeparam>
        /// <param name="items">The monad instance which children are to be selected</param>
        /// <param name="selector">A method that selects the output</param>
        /// <returns>A monad instance with items selected by the given selector function</returns>
        /// <remarks>Please see the documentation of the SelectMany extension method and its use with the LINQ-syntax for more details</remarks>
        public static IObservableSource<T2> Select<T1, T2>(this IObservableSource<T1> items, Func<T1, T2> selector)
        {
            if (items == null) throw new ArgumentNullException("items");
            if (selector == null) throw new ArgumentNullException("selector");
            return new SelectSource<T1, T2>(items, selector);
        }

        /// <summary>
        /// Filters the given monad instance with the given filter operator
        /// </summary>
        /// <typeparam name="T">The inner type of the monad instance</typeparam>
        /// <param name="items">The monad instance that should be filtered</param>
        /// <param name="filter">The filter that should be applied to the monad</param>
        /// <returns>A filtered monad instance</returns>
        /// <remarks>Please see the documentation of the Where extension method and its use with the LINQ-syntax for more details</remarks>
        public static FilteredSource<T> Where<T>(this IObservableSource<T> items, Func<T, bool> filter)
        {
            if (items == null) throw new ArgumentNullException("items");
            if (filter == null) throw new ArgumentNullException("filter");
            return new FilteredSource<T>(items, filter);
        }
    }
}
